Write-Up: Mosquito [Quals]
This challenge starts with a single file, mosquito.pcap. The primary objective is to analyze this network traffic capture to find a hidden flag.
Challenge Attachment
mosquito.pcap: The PCAP file containing all the network traffic for analysis.
Initial Analysis
Upon opening mosquito.pcap in Wireshark, two main types of communication immediately stand out:
- ELF File Transfer: A TCP stream is observed transferring an ELF binary file.
- MQTT Traffic: There is significant communication using the MQTT protocol on TCP port 1883.
Understanding MQTT
Before diving deeper, it’s helpful to understand the protocol in play. MQTT (Message Queuing Telemetry Transport) is a lightweight messaging protocol designed for low-bandwidth, high-latency networks, making it ideal for IoT (Internet of Things) devices.
Key concepts relevant to this challenge include:
- Publish/Subscribe Model: Instead of a client-server model, MQTT uses a publish/subscribe pattern. Clients connect to a central server called a broker.
- Broker: The broker’s job is to receive all messages from clients and then route those messages to other clients that have subscribed to receive them.
- Topics: Clients publish messages to specific “topics” (e.g.,
factory/line1/temp). Other clients can subscribe to these topics to receive any messages published to them. - CONNECT Packet: When a client first connects to a broker, it sends a
CONNECTmessage. This packet can contain authentication details like a username and password. - PUBLISH Packet: To send a message, a client sends a
PUBLISHpacket containing the topic and the message payload.
In this challenge, we see clients publishing messages to topics like factory/line3/ctrl, and these messages appear to be encrypted.
Stream Investigation
1. The ELF File Transfer
An early step is to extract the ELF file from the TCP stream. This can be done using Wireshark’s “Follow TCP Stream” feature and saving the raw data. This extracted file is mlaware from the file listing.
2. The MQTT Traffic
Analyzing the MQTT traffic reveals the following pattern:
CONNECTPacket: A connection packet is sent, which contains authentication credentials: ausernameand apassword.PUBLISHPackets: Multiple publish packets are sent to various topics. The payload of these packets is a JSON object containing encrypted data in hexadecimal format under the"data"key.
The hypothesis from this is that the flag is hidden within these encrypted messages.
Reverse Engineering the Encryption Process
The next challenge is to figure out how to decrypt the payloads. The key and IV for decryption must be reverse-engineered. By observing data patterns (or perhaps from clues in the mlaware ELF file), the following scheme can be deduced:
- Algorithm: AES-128 in CBC (Cipher Block Chaining) mode.
- Encryption Key: The 16-byte key is derived from the SHA-256 hash of the MQTT
username.key = sha256(mqtt_user.encode())[:16]
- Initialization Vector (IV): The 16-byte IV is derived from the SHA-256 hash of the MQTT
passwordconcatenated with the packet’s Unix timestamp.iv = sha256(f"{mqtt_pass}:{timestamp}".encode())[:16]
The Solution Script
To automate the decryption process, a Python script (decript.py) was created. This script performs the following steps:
- Reads the
mosquito.pcapfile usingscapy. - Finds the first MQTT
CONNECTpacket to extract theusernameandpassword. - Iterates through all MQTT
PUBLISHpackets. - For each packet, it extracts the encrypted data and the packet’s timestamp.
- It reconstructs the
keyandivaccording to the discovered scheme. - It decrypts the payload using AES-128-CBC and prints the result.
Here is the code for the solution script:
#!/usr/bin/env python3
import os
# Import with IPv6 disabled
import scapy.config
scapy.config.conf.ipv6_enabled = False
from scapy.all import rdpcap, TCP
import struct, json, hashlib
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
PCAP = "mosquito.pcap"
mqtt_user = None
mqtt_pass = None
def sha256(x):
return hashlib.sha256(x).digest()
# ... (MQTT helper functions as in the original script) ...
def read_varlen(buf, i):
mul = 1
val = 0
while True:
b = buf[i]
i += 1
val += (b & 127) * mul
if not (b & 128):
return val, i
mul *= 128
def extract_mqtt(buf):
out = []
i = 0
while i + 2 <= len(buf):
try:
b0 = buf[i]
rem, j = read_varlen(buf, i + 1)
size = (j - i) + rem
if i + size > len(buf):
break
out.append(buf[i:i+size])
i += size
except:
break
return out
def mqtt_connect(pkt):
i = 1
rem, i = read_varlen(pkt, i)
body = pkt[i:i+rem]
# skip protocol name
pn_len = struct.unpack("!H", body[:2])[0]
p = 2 + pn_len
flags = body[p+1]
p += 4
def read_str(p):
l = struct.unpack("!H", body[p:p+2])[0]
p += 2
s = body[p:p+l]
return s, p+l
_, p = read_str(p) # client id
if flags & 0x04: # will flag
_, p = read_str(p)
_, p = read_str(p)
user = pw = None
if flags & 0x80:
user, p = read_str(p)
if flags & 0x40:
pw, p = read_str(p)
return user, pw
def mqtt_publish(pkt):
i = 1
rem, i = read_varlen(pkt, i)
body = pkt[i:i+rem]
tlen = struct.unpack("!H", body[:2])[0]
topic = body[2:2+tlen].decode(errors="ignore")
payload = body[2+tlen:]
return topic, payload
if __name__ == "__main__":
print("[*] Loading PCAP file:", PCAP)
pkts = rdpcap(PCAP)
print(f"[*] Loaded {len(pkts)} packets")
for p in pkts:
if not p.haslayer(TCP):
continue
tcp = p[TCP]
if tcp.sport != 1883 and tcp.dport != 1883:
continue
if not bytes(tcp.payload):
continue
raw = bytes(tcp.payload)
for mp in extract_mqtt(raw):
ptype = mp[0] >> 4
# -------- CONNECT --------
if ptype == 1 and mqtt_user is None:
try:
u, pw = mqtt_connect(mp)
mqtt_user = u.decode()
mqtt_pass = pw.decode()
print("[+] MQTT USER:", mqtt_user)
print("[+] MQTT PASS:", mqtt_pass)
except:
pass
# -------- PUBLISH --------
if ptype == 3 and mqtt_user:
try:
topic, payload = mqtt_publish(mp)
idx = payload.find(b"{ ")
if idx == -1:
continue
obj = json.loads(payload[idx:].decode())
if "data" not in obj:
continue
ts = int(p.time)
iv = sha256(f"{mqtt_pass}:{ts}".encode())[:16]
key = sha256(mqtt_user.encode())[:16]
blob = bytes.fromhex(obj["data"])
if len(blob) % 16 != 0:
blob = blob[16:]
pt = AES.new(key, AES.MODE_CBC, iv).decrypt(blob)
try:
pt = unpad(pt, 16)
except:
pass
print("\n==============================")
print("[*] time :", ts)
print("[*] topic:", topic)
try:
print(pt.decode())
except:
print(pt)
except Exception as e:
continue
print("\n[*] Done!")
Running the Script to Get the Flag
By running the script, the flag will be revealed from the successfully decrypted messages.
python decript.py